/*
 *  http.c
 *
 *  Very simple HTTP file get.  Supports chunked encoding and SSL.
 *
 * Copyright (c) 2006 Karlan Thomas Mitchell<karlanmitchell@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, is prohibited except by
 * Karlan Thomas Mitchell.
 *
 * THIS SOFTWARE IS PROVIDED BY KARLAN THOMAS MITCHELL AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */ 

#include "http.h"

int wget_file(char * remote_http_location, char * local_file, struct st_wget_gui * gui_data){
	
	char * ip = (char *)malloc( 1024 * sizeof(char) );
	char * server = (char *)malloc( 1024 * sizeof(char) );
	char * location = (char *)malloc( PATH_MAX * sizeof(char) );
	int unsigned short port = 80;

	int is_ssl = 0;

	char * pntr;
	char * loc_pntr;
	char * port_pntr;
	struct in_addr * in_addr_pntr;
	pntr = remote_http_location;

	if(strncasecmp(remote_http_location, "http://", 7) == 0){
		pntr = &pntr[7];
	}
	else if(strncasecmp(remote_http_location, "https://", 8) == 0){
		is_ssl = 1;
		pntr = &pntr[8];
	}
	
	loc_pntr = strchr(pntr, '/');
	if(loc_pntr == NULL){
		strcpy(server, pntr);
		strcpy(location, "/");
		}	
	else{
		strncpy(location, loc_pntr, PATH_MAX);
		strncpy(server, pntr, 1024);
		loc_pntr = strchr(server, '/');
		*loc_pntr = 0;
		}


  	 port_pntr = strchr(server, ':');
   	 if(port_pntr != NULL){
   		port_pntr[0] = 0;
   		*(port_pntr++);
   		port = atoi( port_pntr );
		}
	
	/*
	struct hostent * host_ent;
	host_ent = (struct hostent *)gethostbyname(server);

	
	if(host_ent != NULL)
		snprintf(ip, 1024, "%s", inet_ntoa(  *( struct in_addr*)( host_ent -> h_addr) )  );
	else 
		strcpy(ip, server);*/
	
	return wget_file_bare(is_ssl, server, port, server, location, local_file, gui_data);
	}


int wget_file_bare(int is_ssl, char * ip, int short unsigned port, char * host, char * remote_file, char * local_file, struct st_wget_gui * gui_data){
	struct st_psocket http_socket;

	if(is_ssl == 0)
		http_socket.io_method = IO_BSD;
	else
		http_socket.io_method = IO_OPENSSL;


	if(psocket_connect(&http_socket, ip, port) != 0){
		return -1;
	}

	struct st_mdb protocol_data;
	mdb_init(&protocol_data);	
	
	mdb_set(&protocol_data, "httpheader", "GET ");
	mdb_cat(&protocol_data, "httpheader", remote_file);
	mdb_cat(&protocol_data, "httpheader", " HTTP/1.1");
	
	
	mdb_set(&protocol_data, "Host", host);

	mdb_set(&protocol_data, "Connection", "close");
	
	mdb_set(&protocol_data, "User-Agent", "Karlan's Simple HTTP Client (Beta)");
	

	http_send_message(&http_socket, &protocol_data);
	mdb_reset(&protocol_data);	
	http_read_message(&http_socket, &protocol_data);

	if(strcasecmp(mdb_print(&protocol_data, "httpheader"), "HTTP/1.1 302 Found") == 0)
		return wget_file(mdb_print(&protocol_data, "location"), local_file, gui_data);
	if(strcasecmp(mdb_print(&protocol_data, "httpheader"), "HTTP/1.1 301 Moved Permanently") == 0)
		return wget_file(mdb_print(&protocol_data, "location"), local_file, gui_data);

	if(strcasecmp(mdb_print(&protocol_data, "httpheader"), "HTTP/1.1 404 Not Found") == 0)
		return 404;
	if(strcasecmp(mdb_print(&protocol_data, "httpheader"), "HTTP/1.1 200 OK") != 0)
		return -1;
	
	
	http_read_file(&http_socket, local_file, &protocol_data, gui_data);


	mdb_free(&protocol_data);	
	psocket_close_client(&http_socket);
	return 0;
	}

off_t sock_to_file(FILE * filed, struct st_psocket * socket_data, off_t len){
	off_t read = 0;
	
	ssize_t return_val;
	int block_size = 1024;
	char temp_buf[block_size];

	while(read < len){
		while(read + block_size > len && block_size > 1)
			{block_size--;}
		if(read + block_size > len)
			break;

		return_val = psocket_read(socket_data, temp_buf, block_size);
		if(return_val <= 0)
			break;

		fwrite(&temp_buf, 1, return_val, filed);
		read += return_val;
		}
	return read;
	}
 
off_t sock_to_file_gui(FILE * filed, struct st_psocket * socket_data, off_t len, struct st_wget_gui * gui_data){
	off_t read = 0;
	
	ssize_t return_val;
	int block_size = 1024;
	char temp_buf[block_size];

	while(read < len){
		while(read + block_size > len && block_size > 1)
			{block_size--;}
		if(read + block_size > len)
			break;

		return_val = psocket_read(socket_data, temp_buf, block_size);
		if(return_val <= 0)
			break;

		fwrite(&temp_buf, 1, return_val, filed);
		read += return_val;
        if(gui_data != NULL)
		            *gui_data->bytes_downloaded += return_val;
		}
	return read;
	}
	
int http_read_file(struct st_psocket * socket_data, char * local_file, struct st_mdb * protocol_message, struct st_wget_gui * gui_data){

	int chunked_transfer = 0;
 	FILE * filed = fopen(local_file, "wb");
	if(filed == NULL)
		return -1;
	off_t read = 0;
	off_t to_read = 0;
	off_t file_size = 0;

	size_t buffer_len = 4096;
	char * line_buf   = (char *)malloc( buffer_len *  sizeof(char) );
	

	if(strcasecmp(mdb_print(protocol_message, "Transfer-Encoding"), "chunked") == 0)
		chunked_transfer = 1;
	else
		file_size = atoll(mdb_print(protocol_message, "Content-Length"));

    if(gui_data != NULL)
                *gui_data->is_chunked = chunked_transfer;
    if(gui_data != NULL)
                *gui_data->total_size = file_size;

	ssize_t return_val;

	while(1){
		if(chunked_transfer){
			return_val = psocket_readline_r(socket_data, line_buf, buffer_len);
			to_read = strtoll(line_buf, NULL, 16);
			}
		else
			to_read = file_size;

		if(to_read == 0)
			break;

		if(sock_to_file_gui(filed, socket_data, to_read, gui_data) != to_read)
			break;
		if(!chunked_transfer)
			break;
		
	}

	fclose(filed);
	return 0;
	}


int http_send_message(struct st_psocket * socket_data, struct st_mdb * protocol_message){
	
	char * cur_name;
	char * cur_value;	
	size_t cur_item   = 1;
	size_t numof_items;

	
	psocket_fullsend(socket_data, mdb_print(protocol_message, "httpheader"), strlen( mdb_print(protocol_message, "httpheader")));
 	psocket_fullsend(socket_data, "\x0D\x0A",  2);
	mdb_remove(protocol_message, "httpheader");

	numof_items = mdb_count_items(protocol_message);

	for( ; cur_item <= numof_items ; cur_item++){
		cur_name  = mdb_get_name_by_number(protocol_message, cur_item);
		cur_value = mdb_get_value_by_number(protocol_message, cur_item);
		psocket_fullsend(socket_data, cur_name,   strlen(cur_name));
		psocket_fullsend(socket_data, ": ",       2);
		psocket_fullsend(socket_data, cur_value,  strlen(cur_value));
		psocket_fullsend(socket_data, "\x0D\x0A", 2);
		}

	psocket_fullsend(socket_data, "\x0D\x0A",  2);

	return 1;
	}


int http_read_message(struct st_psocket * socket_data, struct st_mdb * protocol_message){
	size_t buffer_lens = 4096;
	char * name_buf   = (char *)malloc( buffer_lens * sizeof(char) );
	char * value_buf  = (char *)malloc( buffer_lens * sizeof(char) );
	char * line_buf   = (char *)malloc( 2 * buffer_lens *  sizeof(char) );
	ssize_t return_val;
	int function_return = 0;	
	int numof_lines = 0;

	

	if(name_buf  == NULL || 
	   value_buf == NULL || 
           line_buf  == NULL)
		return -1;


	while(1){
		errno = 0;

		return_val = psocket_readline_r(socket_data, line_buf, buffer_lens * 2);

		if(return_val < 0){
			function_return = -1;
			break;
			}

		if(return_val == 0){
  			break;
			}
		
		if(numof_lines == 0){
			strncpy(name_buf, "httpheader", 4096);
			strncpy(value_buf, line_buf, 4096);
			}
		else
			str_splitup(line_buf, name_buf, buffer_lens, value_buf, buffer_lens, ":");


		mdb_set(protocol_message, name_buf, value_buf);



#ifdef DEBUG_ALL_TRAFFIC
 		printf("%s: %s\n", name_buf, value_buf);
#endif 


		numof_lines++;
		if(numof_lines >= 30)
			break;
	
	}
#ifdef DEBUG_ALL_TRAFFIC
 		printf("%s: %s\n", name_buf, value_buf);
#endif 


	free(name_buf);
	free(value_buf);
	free(line_buf);
	return function_return;
	}
